/**
 **************************************************************************
 * @file     tcp_server.c
 * @version  v2.1.1
 * @date     2022-07-22
 * @brief    implement tcp server
 **************************************************************************
 *                       Copyright notice & Disclaimer
 *
 * The software Board Support Package (BSP) that is made available to
 * download from Artery official website is the copyrighted work of Artery.
 * Artery authorizes customers to use, copy, and distribute the BSP
 * software and its related documentation for the purpose of design and
 * development in conjunction with Artery microcontrollers. Use of the
 * software is governed by this copyright notice and the following disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
 * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
 * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
 * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
 * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
 *
 **************************************************************************
 */

#include "tcp_server.h"
#include "lwip/err.h"
#include "lwip/tcp.h"
#include "string.h"
#include "mbcom.h"
#include "mbapp.h"
#include "bsp_api.h"
#include "common.h"
#include "os_obj.h"

mb_obj_type mb_server_obj;
void mb_tcp_server_init(void);
struct tcp_pcb *pcb_now;

/**
 * @brief  receive and transmit tcp data
 * @param  arg: the user argument
 * @param  pcb: the tcp_pcb that has received the data
 * @param  p: the packet buffer
 * @param  err: the error value linked with the received data
 * @retval error value
 */
static err_t tcp_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
	char *pPrintBuf;
	if (p != NULL)
	{
		pcb_now = pcb;

		pPrintBuf = (char *)malloc(p->tot_len * sizeof(char));
		memset(pPrintBuf, 0, p->tot_len * sizeof(char));

		memcpy(mb_server_obj.dat_buff, p->payload, p->tot_len);

		mb_tcp_rcv(&mb_server_obj, p->payload, p->tot_len);
		// strncpy(pPrintBuf, p->payload, p->tot_len);

		tcp_recved(pcb, p->tot_len); /* Get data length; tot_len: length of tcp data block */
		// tcp_write(pcb, p->payload, p->tot_len, TCP_WRITE_FLAG_COPY); /* payload is starting position of TCP data block */
		tcp_output(pcb);
		tx_semaphore_put(os_obj.sid_eth);
		
		// tcp_write(pcb, QUIZ_MESSAGE, strlen(QUIZ_MESSAGE), 1);
	}
	else
	{
		tcp_close(pcb); /* TCP server shouldn't close this session actively */
	}
	pbuf_free(p); /* free the TCP segment */
	free(pPrintBuf);
	err = ERR_OK;
	return err;
}

/**
 * @brief  callback function for receiving data
 * @param  arg: user supplied argument
 * @param  pcb: the tcp_pcb which accepted the connection
 * @param  err: error value
 * @retval error value
 */
static err_t tcp_server_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
	tcp_setprio(pcb, TCP_PRIO_MIN); /* set the priority of callback function, if there are multiple session exist, this function must be called */
	tcp_recv(pcb, tcp_server_recv); /* set callbacl function for TCP segments come in */

	// tcp_write(pcb, QUIZ_MESSAGE, strlen(QUIZ_MESSAGE), 1);
	dbg_print("NEW CLIENT LINK IP: %d, %d, %d, %d :: %d\n", 
	byte_lb(pcb->remote_ip.addr), byte_hb(pcb->remote_ip.addr), 
	byte_ub(pcb->remote_ip.addr), byte_mb(pcb->remote_ip.addr),
	pcb->remote_port);

	err = ERR_OK;
	return err;
}

/**
 * @brief  initialize tcp server
 * @param  none
 * @retval none
 */
void tcp_server_init(void)
{
	struct tcp_pcb *pcb;
	err_t err;
	/*****************************************************/
	pcb = tcp_new(); /* create TCP protocol control block for communication */
	if (!pcb)
	{
		return;
	}
	err = tcp_bind(pcb, IP_ADDR_ANY, TCP_SERVER_LOCAL_PORT); /* bind local IP and port for TCP server */
	if (err != ERR_OK)
	{
		return;
	}
	pcb = tcp_listen(pcb);				/* entering listening status */
	tcp_accept(pcb, tcp_server_accept); /* set callback function for connection requestion */

	mb_tcp_server_init();
}

/**
 * @brief  tcp_adu_send
 * @param  none
 * @retval none
 */
static void tcp_adu_send(void)
{
	tcp_write(pcb_now, mb_server_obj.dat_buff, mb_server_obj.snd_size, TCP_WRITE_FLAG_COPY);
	mb_send_end(&mb_server_obj);
}

/**
 * @brief  tcp_mb_timer
 * @param  none
 * @retval none
 */
void mb_tcp_server_timer(void)
{
	mb_rtu_timer_run(&mb_server_obj);
}

/**
 * @brief  initialize tcp server
 * @param  none
 * @retval none
 */
void mb_tcp_server_init(void)
{
	static mb_cmd_buff_type cmdbuff[6];
	mb_obj_init(&mb_server_obj);
	mmb_cmd_buff_init(&mb_server_obj, cmdbuff, 6, 3);
	mb_server_obj.run_st.bits.rtu_mode = 0;
	mb_server_obj.run_st.bits.tcp_mode = 1;
	mb_server_obj.slave.reg_input_cb = smb_reg_input_cb;
	mb_server_obj.slave.reg_hold_cb = smb_reg_holding_cb;
	mb_server_obj.slave.reg_coils_cb = smb_reg_coils_cb;
	mb_server_obj.slave.reg_discrete_cb = smb_reg_discrete_cb;
	mb_server_obj.adu_send = tcp_adu_send;
	mb_server_obj.id0_ack_en = 1;
	mb_server_obj.slave_id = 1;

	app_timer_task_add(mb_tcp_server_timer);
}

/**
 * @brief  mb server poll
 * @param  none
 * @retval none
 */
void mb_tcp_server_poll(void)
{
	mb_poll(&mb_server_obj);
}
